#include "basechart_selection.h"

#include "basechart_axis.h"
#include "basechart_plot.h"
#include "basechart_plottable.h"

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartRange
////////////////////////////////////////////////////////////////////////////////////////////////////
/*! \class BaseChartRange
  \brief Represents the range an axis is encompassing.

  contains a \a lower and \a upper double value and provides convenience input,
  output and modification functions.

  \see BaseChartAxis::setRange
*/

/* start of documentation of inline functions */

/*! \fn double BaseChartRange::size() const

  Returns the size of the range, i.e. \a upper-\a lower
*/

/*! \fn double BaseChartRange::center() const

  Returns the center of the range, i.e. (\a upper+\a lower)*0.5
*/

/*! \fn void BaseChartRange::normalize()

  Makes sure \a lower is numerically smaller than \a upper. If this is not the
  case, the values are swapped.
*/

/*! \fn bool BaseChartRange::contains(double value) const

  Returns true when \a value lies within or exactly on the borders of the range.
*/

/*! \fn BaseChartRange &BaseChartRange::operator+=(const double& value)

  Adds \a value to both boundaries of the range.
*/

/*! \fn BaseChartRange &BaseChartRange::operator-=(const double& value)

  Subtracts \a value from both boundaries of the range.
*/

/*! \fn BaseChartRange &BaseChartRange::operator*=(const double& value)

  Multiplies both boundaries of the range by \a value.
*/

/*! \fn BaseChartRange &BaseChartRange::operator/=(const double& value)

  Divides both boundaries of the range by \a value.
*/

/* end of documentation of inline functions */

/*!
  Minimum range size (\a upper - \a lower) the range changing functions will
  accept. Smaller intervals would cause errors due to the 11-bit exponent of
  double precision numbers, corresponding to a minimum magnitude of roughly
  1e-308.

  \warning Do not use this constant to indicate "arbitrarily small" values in
  plotting logic (as values that will appear in the plot)! It is intended only
  as a bound to compare against, e.g. to prevent axis ranges from obtaining
  underflowing ranges.

  \see validRange, maxRange
*/
const double BaseChartRange::minRange = 1e-280;

/*!
  Maximum values (negative and positive) the range will accept in range-changing
  functions. Larger absolute values would cause errors due to the 11-bit
  exponent of double precision numbers, corresponding to a maximum magnitude of
  roughly 1e308.

  \warning Do not use this constant to indicate "arbitrarily large" values in
  plotting logic (as values that will appear in the plot)! It is intended only
  as a bound to compare against, e.g. to prevent axis ranges from obtaining
  overflowing ranges.

  \see validRange, minRange
*/
const double BaseChartRange::maxRange = 1e250;

/*!
  Constructs a range with \a lower and \a upper set to zero.
*/
BaseChartRange::BaseChartRange() : lower(0), upper(0) {}

/*! \overload

  Constructs a range with the specified \a lower and \a upper values.

  The resulting range will be normalized (see \ref normalize), so if \a lower is
  not numerically smaller than \a upper, they will be swapped.
*/
BaseChartRange::BaseChartRange(double lower, double upper)
    : lower(lower), upper(upper) {
  normalize();
}

/*! \overload

  Expands this range such that \a otherRange is contained in the new range. It
  is assumed that both this range and \a otherRange are normalized (see \ref
  normalize).

  If this range contains NaN as lower or upper bound, it will be replaced by the
  respective bound of \a otherRange.

  If \a otherRange is already inside the current range, this function does
  nothing.

  \see expanded
*/
void BaseChartRange::expand(const BaseChartRange& otherRange) {
  if (lower > otherRange.lower || qIsNaN(lower)) lower = otherRange.lower;
  if (upper < otherRange.upper || qIsNaN(upper)) upper = otherRange.upper;
}

/*! \overload

  Expands this range such that \a includeCoord is contained in the new range. It
  is assumed that this range is normalized (see \ref normalize).

  If this range contains NaN as lower or upper bound, the respective bound will
  be set to \a includeCoord.

  If \a includeCoord is already inside the current range, this function does
  nothing.

  \see expand
*/
void BaseChartRange::expand(double includeCoord) {
  if (lower > includeCoord || qIsNaN(lower)) lower = includeCoord;
  if (upper < includeCoord || qIsNaN(upper)) upper = includeCoord;
}

/*! \overload

  Returns an expanded range that contains this and \a otherRange. It is assumed
  that both this range and \a otherRange are normalized (see \ref normalize).

  If this range contains NaN as lower or upper bound, the returned range's bound
  will be taken from \a otherRange.

  \see expand
*/
BaseChartRange BaseChartRange::expanded(
    const BaseChartRange& otherRange) const {
  BaseChartRange result = *this;
  result.expand(otherRange);
  return result;
}

/*! \overload

  Returns an expanded range that includes the specified \a includeCoord. It is
  assumed that this range is normalized (see \ref normalize).

  If this range contains NaN as lower or upper bound, the returned range's bound
  will be set to \a includeCoord.

  \see expand
*/
BaseChartRange BaseChartRange::expanded(double includeCoord) const {
  BaseChartRange result = *this;
  result.expand(includeCoord);
  return result;
}

/*!
  Returns this range, possibly modified to not exceed the bounds provided as \a
  lowerBound and \a upperBound. If possible, the size of the current range is
  preserved in the process.

  If the range shall only be bounded at the lower side, you can set \a
  upperBound to \ref BaseChartRange::maxRange. If it shall only be bounded at
  the upper side, set \a lowerBound to -\ref BaseChartRange::maxRange.
*/
BaseChartRange BaseChartRange::bounded(double lowerBound,
                                       double upperBound) const {
  if (lowerBound > upperBound) qSwap(lowerBound, upperBound);

  BaseChartRange result(lower, upper);
  if (result.lower < lowerBound) {
    result.lower = lowerBound;
    result.upper = lowerBound + size();
    if (result.upper > upperBound ||
        qFuzzyCompare(size(), upperBound - lowerBound))
      result.upper = upperBound;
  } else if (result.upper > upperBound) {
    result.upper = upperBound;
    result.lower = upperBound - size();
    if (result.lower < lowerBound ||
        qFuzzyCompare(size(), upperBound - lowerBound))
      result.lower = lowerBound;
  }

  return result;
}

/*!
  Returns a sanitized version of the range. Sanitized means for logarithmic
  scales, that the range won't span the positive and negative sign domain, i.e.
  contain zero. Further \a lower will always be numerically smaller (or equal)
  to \a upper.

  If the original range does span positive and negative sign domains or contains
  zero, the returned range will try to approximate the original range as good as
  possible. If the positive interval of the original range is wider than the
  negative interval, the returned range will only contain the positive interval,
  with lower bound set to \a rangeFac or \a rangeFac *\a upper, whichever is
  closer to zero. Same procedure is used if the negative interval is wider than
  the positive interval, this time by changing the \a upper bound.
*/
BaseChartRange BaseChartRange::sanitizedForLogScale() const {
  double rangeFac = 1e-3;
  BaseChartRange sanitizedRange(lower, upper);
  sanitizedRange.normalize();
  // can't have range spanning negative and positive values in log plot, so
  // change range to fix it
  // if (qFuzzyCompare(sanitizedRange.lower+1, 1) &&
  // !qFuzzyCompare(sanitizedRange.upper+1, 1))
  if (sanitizedRange.lower == 0.0 && sanitizedRange.upper != 0.0) {
    // case lower is 0
    if (rangeFac < sanitizedRange.upper * rangeFac)
      sanitizedRange.lower = rangeFac;
    else
      sanitizedRange.lower = sanitizedRange.upper * rangeFac;
  }  // else if (!qFuzzyCompare(lower+1, 1) && qFuzzyCompare(upper+1, 1))
  else if (sanitizedRange.lower != 0.0 && sanitizedRange.upper == 0.0) {
    // case upper is 0
    if (-rangeFac > sanitizedRange.lower * rangeFac)
      sanitizedRange.upper = -rangeFac;
    else
      sanitizedRange.upper = sanitizedRange.lower * rangeFac;
  } else if (sanitizedRange.lower < 0 && sanitizedRange.upper > 0) {
    // find out whether negative or positive interval is wider to decide which
    // sign domain will be chosen
    if (-sanitizedRange.lower > sanitizedRange.upper) {
      // negative is wider, do same as in case upper is 0
      if (-rangeFac > sanitizedRange.lower * rangeFac)
        sanitizedRange.upper = -rangeFac;
      else
        sanitizedRange.upper = sanitizedRange.lower * rangeFac;
    } else {
      // positive is wider, do same as in case lower is 0
      if (rangeFac < sanitizedRange.upper * rangeFac)
        sanitizedRange.lower = rangeFac;
      else
        sanitizedRange.lower = sanitizedRange.upper * rangeFac;
    }
  }
  // due to normalization, case lower>0 && upper<0 should never occur, because
  // that implies upper<lower
  return sanitizedRange;
}

/*!
  Returns a sanitized version of the range. Sanitized means for linear scales,
  that \a lower will always be numerically smaller (or equal) to \a upper.
*/
BaseChartRange BaseChartRange::sanitizedForLinScale() const {
  BaseChartRange sanitizedRange(lower, upper);
  sanitizedRange.normalize();
  return sanitizedRange;
}

/*!
  Checks, whether the specified range is within valid bounds, which are defined
  as BaseChartRange::maxRange and BaseChartRange::minRange.
  A valid range means:
  \li range bounds within -maxRange and maxRange
  \li range size above minRange
  \li range size below maxRange
*/
bool BaseChartRange::validRange(double lower, double upper) {
  return (lower > -maxRange && upper < maxRange &&
          qAbs(lower - upper) > minRange && qAbs(lower - upper) < maxRange &&
          !(lower > 0 && qIsInf(upper / lower)) &&
          !(upper < 0 && qIsInf(lower / upper)));
}

/*!
  \overload
  Checks, whether the specified range is within valid bounds, which are defined
  as BaseChartRange::maxRange and BaseChartRange::minRange.
  A valid range means:
  \li range bounds within -maxRange and maxRange
  \li range size above minRange
  \li range size below maxRange
*/
bool BaseChartRange::validRange(const BaseChartRange& range) {
  return (range.lower > -maxRange && range.upper < maxRange &&
          qAbs(range.lower - range.upper) > minRange &&
          qAbs(range.lower - range.upper) < maxRange &&
          !(range.lower > 0 && qIsInf(range.upper / range.lower)) &&
          !(range.upper < 0 && qIsInf(range.lower / range.upper)));
}
/* end of 'src/axis/range.cpp' */

/* including file 'src/selection.cpp'       */
/* modified 2021-03-29T02:30:44, size 21837 */

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartDataRange
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartDataRange
  \brief Describes a data range given by begin and end index

  BaseChartDataRange holds two integers describing the begin (\ref setBegin) and
  end (\ref setEnd) index of a contiguous set of data points. The \a end index
  corresponds to the data point just after the last data point of the data
  range, like in standard iterators.

  Data Ranges are not bound to a certain plottable, thus they can be freely
  exchanged, created and modified. If a non-contiguous data set shall be
  described, the class \ref BaseChartDataSelection is used, which holds and
  manages multiple instances of \ref BaseChartDataRange. In most situations,
  \ref BaseChartDataSelection is thus used.

  Both \ref BaseChartDataRange and \ref BaseChartDataSelection offer convenience
  methods to work with them, e.g. \ref bounded, \ref expanded, \ref intersects,
  \ref intersection, \ref adjusted, \ref contains. Further, addition and
  subtraction operators (defined in \ref BaseChartDataSelection) can be used to
  join/subtract data ranges and data selections (or mixtures), to retrieve a
  corresponding \ref BaseChartDataSelection.

  %BaseChartCustomPlot's \ref dataselection "data selection mechanism" is based
  on \ref BaseChartDataSelection and BaseChartDataRange.

  \note Do not confuse \ref BaseChartDataRange with \ref BaseChartRange. A \ref
  BaseChartRange describes an interval in floating point plot coordinates, e.g.
  the current axis range.
*/

/* start documentation of inline functions */

/*! \fn int BaseChartDataRange::size() const

  Returns the number of data points described by this data range. This is equal
  to the end index minus the begin index.

  \see length
*/

/*! \fn int BaseChartDataRange::length() const

  Returns the number of data points described by this data range. Equivalent to
  \ref size.
*/

/*! \fn void BaseChartDataRange::setBegin(int begin)

  Sets the begin of this data range. The \a begin index points to the first data
  point that is part of the data range.

  No checks or corrections are made to ensure the resulting range is valid (\ref
  isValid).

  \see setEnd
*/

/*! \fn void BaseChartDataRange::setEnd(int end)

  Sets the end of this data range. The \a end index points to the data point
  just after the last data point that is part of the data range.

  No checks or corrections are made to ensure the resulting range is valid (\ref
  isValid).

  \see setBegin
*/

/*! \fn bool BaseChartDataRange::isValid() const

  Returns whether this range is valid. A valid range has a begin index greater
  or equal to 0, and an end index greater or equal to the begin index.

  \note Invalid ranges should be avoided and are never the result of any of
  BaseChartCustomPlot's methods (unless they are themselves fed with invalid
  ranges). Do not pass invalid ranges to BaseChartCustomPlot's methods. The
  invalid range is not inherently prevented in BaseChartDataRange, to allow
  temporary invalid begin/end values while manipulating the range. An invalid
  range is not necessarily empty
  (\ref isEmpty), since its \ref length can be negative and thus non-zero.
*/

/*! \fn bool BaseChartDataRange::isEmpty() const

  Returns whether this range is empty, i.e. whether its begin index equals its
  end index.

  \see size, length
*/

/*! \fn BaseChartDataRange BaseChartDataRange::adjusted(int changeBegin, int
  changeEnd) const

  Returns a data range where \a changeBegin and \a changeEnd were added to the
  begin and end indices, respectively.
*/

/* end documentation of inline functions */

/*!
  Creates an empty BaseChartDataRange, with begin and end set to 0.
*/
BaseChartDataRange::BaseChartDataRange() : mBegin(0), mEnd(0) {}

/*!
  Creates a BaseChartDataRange, initialized with the specified \a begin and \a
  end.

  No checks or corrections are made to ensure the resulting range is valid (\ref
  isValid).
*/
BaseChartDataRange::BaseChartDataRange(int begin, int end)
    : mBegin(begin), mEnd(end) {}

/*!
  Returns a data range that matches this data range, except that parts exceeding
  \a other are excluded.

  This method is very similar to \ref intersection, with one distinction: If
  this range and the \a other range share no intersection, the returned data
  range will be empty with begin and end set to the respective boundary side of
  \a other, at which this range is residing. (\ref intersection would just
  return a range with begin and end set to 0.)
*/
BaseChartDataRange BaseChartDataRange::bounded(
    const BaseChartDataRange& other) const {
  BaseChartDataRange result(intersection(other));
  if (result.isEmpty())  // no intersection, preserve respective bounding side
                         // of otherRange as both begin and end of return value
  {
    if (mEnd <= other.mBegin)
      result = BaseChartDataRange(other.mBegin, other.mBegin);
    else
      result = BaseChartDataRange(other.mEnd, other.mEnd);
  }
  return result;
}

/*!
  Returns a data range that contains both this data range as well as \a other.
*/
BaseChartDataRange BaseChartDataRange::expanded(
    const BaseChartDataRange& other) const {
  return {qMin(mBegin, other.mBegin), qMax(mEnd, other.mEnd)};
}

/*!
  Returns the data range which is contained in both this data range and \a
  other.

  This method is very similar to \ref bounded, with one distinction: If this
  range and the \a other range share no intersection, the returned data range
  will be empty with begin and end set to 0.
  (\ref bounded would return a range with begin and end set to one of the
  boundaries of \a other, depending on which side this range is on.)

  \see BaseChartDataSelection::intersection
*/
BaseChartDataRange BaseChartDataRange::intersection(
    const BaseChartDataRange& other) const {
  BaseChartDataRange result(qMax(mBegin, other.mBegin), qMin(mEnd, other.mEnd));
  if (result.isValid())
    return result;
  else
    return {};
}

/*!
  Returns whether this data range and \a other share common data points.

  \see intersection, contains
*/
bool BaseChartDataRange::intersects(const BaseChartDataRange& other) const {
  return !((mBegin > other.mBegin && mBegin >= other.mEnd) ||
           (mEnd <= other.mBegin && mEnd < other.mEnd));
}

/*!
  Returns whether all data points of \a other are also contained inside this
  data range.

  \see intersects
*/
bool BaseChartDataRange::contains(const BaseChartDataRange& other) const {
  return mBegin <= other.mBegin && mEnd >= other.mEnd;
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartDataSelection
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartDataSelection
  \brief Describes a data set by holding multiple BaseChartDataRange instances

  BaseChartDataSelection manages multiple instances of BaseChartDataRange in
  order to represent any (possibly disjoint) set of data selection.

  The data selection can be modified with addition and subtraction operators
  which take BaseChartDataSelection and BaseChartDataRange instances, as well as
  methods such as \ref addDataRange and \ref clear. Read access is provided by
  \ref dataRange, \ref dataRanges, \ref dataRangeCount, etc.

  The method \ref simplify is used to join directly adjacent or even overlapping
  BaseChartDataRange instances. BaseChartDataSelection automatically simplifies
  when using the addition/subtraction operators. The only case when \ref
  simplify is left to the user, is when calling \ref addDataRange, with the
  parameter \a simplify explicitly set to false. This is useful if many data
  ranges will be added to the selection successively and the overhead for
  simplifying after each iteration shall be avoided. In this case, you should
  make sure to call \ref simplify after completing the operation.

  Use \ref enforceType to bring the data selection into a state complying with
  the constraints for selections defined in \ref SelectionType.

  %BaseChartCustomPlot's \ref dataselection "data selection mechanism" is based
  on BaseChartDataSelection and BaseChartDataRange.

  \section qcpdataselection-iterating Iterating over a data selection

  As an example, the following code snippet calculates the average value of a
  graph's data \ref BaseChartAbstractPlottable::selection "selection":

  \snippet documentation/doc-code-snippets/mainwindow.cpp
  qcpdataselection-iterating-1

*/

/* start documentation of inline functions */

/*! \fn int BaseChartDataSelection::dataRangeCount() const

  Returns the number of ranges that make up the data selection. The ranges can
  be accessed by \ref dataRange via their index.

  \see dataRange, dataPointCount
*/

/*! \fn QList<BaseChartDataRange> BaseChartDataSelection::dataRanges() const

  Returns all data ranges that make up the data selection. If the data selection
  is simplified (the usual state of the selection, see \ref simplify), the
  ranges are sorted by ascending data point index.

  \see dataRange
*/

/*! \fn bool BaseChartDataSelection::isEmpty() const

  Returns true if there are no data ranges, and thus no data points, in this
  BaseChartDataSelection instance.

  \see dataRangeCount
*/

/* end documentation of inline functions */

/*!
  Creates an empty BaseChartDataSelection.
*/
BaseChartDataSelection::BaseChartDataSelection() {}

/*!
  Creates a BaseChartDataSelection containing the provided \a range.
*/
BaseChartDataSelection::BaseChartDataSelection(
    const BaseChartDataRange& range) {
  mDataRanges.append(range);
}

/*!
  Returns true if this selection is identical (contains the same data ranges
  with the same begin and end indices) to \a other.

  Note that both data selections must be in simplified state (the usual state of
  the selection, see \ref simplify) for this operator to return correct results.
*/
bool BaseChartDataSelection::operator==(
    const BaseChartDataSelection& other) const {
  if (mDataRanges.size() != other.mDataRanges.size()) return false;
  for (int i = 0; i < mDataRanges.size(); ++i) {
    if (mDataRanges.at(i) != other.mDataRanges.at(i)) return false;
  }
  return true;
}

/*!
  Adds the data selection of \a other to this data selection, and then
  simplifies this data selection (see \ref simplify).
*/
BaseChartDataSelection& BaseChartDataSelection::operator+=(
    const BaseChartDataSelection& other) {
  mDataRanges << other.mDataRanges;
  simplify();
  return *this;
}

/*!
  Adds the data range \a other to this data selection, and then simplifies this
  data selection (see \ref simplify).
*/
BaseChartDataSelection& BaseChartDataSelection::operator+=(
    const BaseChartDataRange& other) {
  addDataRange(other);
  return *this;
}

/*!
  Removes all data point indices that are described by \a other from this data
  selection.
*/
BaseChartDataSelection& BaseChartDataSelection::operator-=(
    const BaseChartDataSelection& other) {
  for (int i = 0; i < other.dataRangeCount(); ++i) *this -= other.dataRange(i);

  return *this;
}

/*!
  Removes all data point indices that are described by \a other from this data
  selection.
*/
BaseChartDataSelection& BaseChartDataSelection::operator-=(
    const BaseChartDataRange& other) {
  if (other.isEmpty() || isEmpty()) return *this;

  simplify();
  int i = 0;
  while (i < mDataRanges.size()) {
    const int thisBegin = mDataRanges.at(i).begin();
    const int thisEnd = mDataRanges.at(i).end();
    if (thisBegin >= other.end())
      break;  // since data ranges are sorted after the simplify() call, no
              // ranges which contain other will come after this

    if (thisEnd > other.begin())  // ranges which don't fulfill this are
                                  // entirely before other and can be ignored
    {
      if (thisBegin >= other.begin())  // range leading segment is encompassed
      {
        if (thisEnd <=
            other.end())  // range fully encompassed, remove completely
        {
          mDataRanges.removeAt(i);
          continue;
        } else  // only leading segment is encompassed, trim accordingly
          mDataRanges[i].setBegin(other.end());
      } else  // leading segment is not encompassed
      {
        if (thisEnd <= other.end())  // only trailing segment is encompassed,
                                     // trim accordingly
        {
          mDataRanges[i].setEnd(other.begin());
        } else  // other lies inside this range, so split range
        {
          mDataRanges[i].setEnd(other.begin());
          mDataRanges.insert(i + 1, BaseChartDataRange(other.end(), thisEnd));
          break;  // since data ranges are sorted (and don't overlap) after
                  // simplify() call, we're done here
        }
      }
    }
    ++i;
  }

  return *this;
}

/*!
  Returns the total number of data points contained in all data ranges that make
  up this data selection.
*/
int BaseChartDataSelection::dataPointCount() const {
  int result = 0;
  foreach (BaseChartDataRange dataRange, mDataRanges)
    result += dataRange.length();
  return result;
}

/*!
  Returns the data range with the specified \a index.

  If the data selection is simplified (the usual state of the selection, see
  \ref simplify), the ranges are sorted by ascending data point index.

  \see dataRangeCount
*/
BaseChartDataRange BaseChartDataSelection::dataRange(int index) const {
  if (index >= 0 && index < mDataRanges.size()) {
    return mDataRanges.at(index);
  } else {
    qDebug() << Q_FUNC_INFO << "index out of range:" << index;
    return {};
  }
}

/*!
  Returns a \ref BaseChartDataRange which spans the entire data selection,
  including possible intermediate segments which are not part of the original
  data selection.
*/
BaseChartDataRange BaseChartDataSelection::span() const {
  if (isEmpty())
    return {};
  else
    return {mDataRanges.first().begin(), mDataRanges.last().end()};
}

/*!
  Adds the given \a dataRange to this data selection. This is equivalent to the
  += operator but allows disabling immediate simplification by setting \a
  simplify to false. This can improve performance if adding a very large amount
  of data ranges successively. In this case, make sure to call \ref simplify
  manually, after the operation.
*/
void BaseChartDataSelection::addDataRange(const BaseChartDataRange& dataRange,
                                          bool simplify) {
  mDataRanges.append(dataRange);
  if (simplify) this->simplify();
}

/*!
  Removes all data ranges. The data selection then contains no data points.

  \ref isEmpty
*/
void BaseChartDataSelection::clear() { mDataRanges.clear(); }

/*!
  Sorts all data ranges by range begin index in ascending order, and then joins
  directly adjacent or overlapping ranges. This can reduce the number of
  individual data ranges in the selection, and prevents possible double-counting
  when iterating over the data points held by the data ranges.

  This method is automatically called when using the addition/subtraction
  operators. The only case when \ref simplify is left to the user, is when
  calling \ref addDataRange, with the parameter \a simplify explicitly set to
  false.
*/
void BaseChartDataSelection::simplify() {
  // remove any empty ranges:
  for (int i = mDataRanges.size() - 1; i >= 0; --i) {
    if (mDataRanges.at(i).isEmpty()) mDataRanges.removeAt(i);
  }
  if (mDataRanges.isEmpty()) return;

  // sort ranges by starting value, ascending:
  std::sort(mDataRanges.begin(), mDataRanges.end(), lessThanDataRangeBegin);

  // join overlapping/contiguous ranges:
  int i = 1;
  while (i < mDataRanges.size()) {
    if (mDataRanges.at(i - 1).end() >=
        mDataRanges.at(i)
            .begin())  // range i overlaps/joins with i-1, so expand range i-1
                       // appropriately and remove range i from list
    {
      mDataRanges[i - 1].setEnd(
          qMax(mDataRanges.at(i - 1).end(), mDataRanges.at(i).end()));
      mDataRanges.removeAt(i);
    } else
      ++i;
  }
}

/*!
  Makes sure this data selection conforms to the specified \a type selection
  type. Before the type is enforced, \ref simplify is called.

  Depending on \a type, enforcing means adding new data points that were
  previously not part of the selection, or removing data points from the
  selection. If the current selection already conforms to \a type, the data
  selection is not changed.

  \see SelectionType
*/
void BaseChartDataSelection::enforceType(SelectionType type) {
  simplify();
  switch (type) {
    case stNone: {
      mDataRanges.clear();
      break;
    }
    case stWhole: {
      // whole selection isn't defined by data range, so don't change anything
      // (is handled in plottable methods)
      break;
    }
    case stSingleData: {
      // reduce all data ranges to the single first data point:
      if (!mDataRanges.isEmpty()) {
        if (mDataRanges.size() > 1)
          mDataRanges = QList<BaseChartDataRange>() << mDataRanges.first();
        if (mDataRanges.first().length() > 1)
          mDataRanges.first().setEnd(mDataRanges.first().begin() + 1);
      }
      break;
    }
    case stDataRange: {
      if (!isEmpty()) mDataRanges = QList<BaseChartDataRange>() << span();
      break;
    }
    case stMultipleDataRanges: {
      // this is the selection type that allows all concievable combinations of
      // ranges, so do nothing
      break;
    }
  }
}

/*!
  Returns true if the data selection \a other is contained entirely in this data
  selection, i.e. all data point indices that are in \a other are also in this
  data selection.

  \see BaseChartDataRange::contains
*/
bool BaseChartDataSelection::contains(
    const BaseChartDataSelection& other) const {
  if (other.isEmpty()) return false;

  int otherIndex = 0;
  int thisIndex = 0;
  while (thisIndex < mDataRanges.size() &&
         otherIndex < other.mDataRanges.size()) {
    if (mDataRanges.at(thisIndex).contains(other.mDataRanges.at(otherIndex)))
      ++otherIndex;
    else
      ++thisIndex;
  }
  return thisIndex <
         mDataRanges.size();  // if thisIndex ran all the way to the end to find
                              // a containing range for the current otherIndex,
                              // other is not contained in this
}

/*!
  Returns a data selection containing the points which are both in this data
  selection and in the data range \a other.

  A common use case is to limit an unknown data selection to the valid range of
  a data container, using \ref QCPDataContainer::dataRange as \a other. One can
  then safely iterate over the returned data selection without exceeding the
  data container's bounds.
*/
BaseChartDataSelection BaseChartDataSelection::intersection(
    const BaseChartDataRange& other) const {
  BaseChartDataSelection result;
  foreach (BaseChartDataRange dataRange, mDataRanges)
    result.addDataRange(dataRange.intersection(other), false);
  result.simplify();
  return result;
}

/*!
  Returns a data selection containing the points which are both in this data
  selection and in the data selection \a other.
*/
BaseChartDataSelection BaseChartDataSelection::intersection(
    const BaseChartDataSelection& other) const {
  BaseChartDataSelection result;
  for (int i = 0; i < other.dataRangeCount(); ++i)
    result += intersection(other.dataRange(i));
  result.simplify();
  return result;
}

/*!
  Returns a data selection which is the exact inverse of this data selection,
  with \a outerRange defining the base range on which to invert. If \a
  outerRange is smaller than the \ref span of this data selection, it is
  expanded accordingly.

  For example, this method can be used to retrieve all unselected segments by
  setting \a outerRange to the full data range of the plottable, and calling
  this method on a data selection holding the selected segments.
*/
BaseChartDataSelection BaseChartDataSelection::inverse(
    const BaseChartDataRange& outerRange) const {
  if (isEmpty()) return BaseChartDataSelection(outerRange);
  BaseChartDataRange fullRange = outerRange.expanded(span());

  BaseChartDataSelection result;
  // first unselected segment:
  if (mDataRanges.first().begin() != fullRange.begin())
    result.addDataRange(
        BaseChartDataRange(fullRange.begin(), mDataRanges.first().begin()),
        false);
  // intermediate unselected segments:
  for (int i = 1; i < mDataRanges.size(); ++i)
    result.addDataRange(BaseChartDataRange(mDataRanges.at(i - 1).end(),
                                           mDataRanges.at(i).begin()),
                        false);
  // last unselected segment:
  if (mDataRanges.last().end() != fullRange.end())
    result.addDataRange(
        BaseChartDataRange(mDataRanges.last().end(), fullRange.end()), false);
  result.simplify();
  return result;
}
/* end of 'src/selection.cpp' */

/* including file 'src/selectionrect.cpp'  */
/* modified 2021-03-29T02:30:44, size 9215 */

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartSelectionRect
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartSelectionRect
  \brief Provides rect/rubber-band data selection and range zoom interaction

  BaseChartSelectionRect is used by BaseChartCustomPlot when the \ref
  BaseChartCustomPlot::setSelectionRectMode is not \ref srmNone. When the user
  drags the mouse across the plot, the current selection rect instance (\ref
  BaseChartCustomPlot::setSelectionRect) is forwarded these events and makes
  sure an according rect shape is drawn. At the begin, during, and after
  completion of the interaction, it emits the corresponding signals \ref
  started, \ref changed, \ref canceled, and \ref accepted.

  The BaseChartCustomPlot instance connects own slots to the current selection
  rect instance, in order to react to an accepted selection rect interaction
  accordingly.

  \ref isActive can be used to check whether the selection rect is currently
  active. An ongoing selection interaction can be cancelled programmatically via
  calling \ref cancel at any time.

  The appearance of the selection rect can be controlled via \ref setPen and
  \ref setBrush.

  If you wish to provide custom behaviour, e.g. a different visual
  representation of the selection rect (\ref BaseChartSelectionRect::draw), you
  can subclass BaseChartSelectionRect and pass an instance of your subclass to
  \ref BaseChartCustomPlot::setSelectionRect.
*/

/* start of documentation of inline functions */

/*! \fn bool BaseChartSelectionRect::isActive() const

  Returns true if there is currently a selection going on, i.e. the user has
  started dragging a selection rect, but hasn't released the mouse button yet.

  \see cancel
*/

/* end of documentation of inline functions */
/* start documentation of signals */

/*! \fn void BaseChartSelectionRect::started(QMouseEvent *event);

  This signal is emitted when a selection rect interaction was initiated, i.e.
  the user just started dragging the selection rect with the mouse.
*/

/*! \fn void BaseChartSelectionRect::changed(const QRect &rect, QMouseEvent
  *event);

  This signal is emitted while the selection rect interaction is ongoing and the
  \a rect has changed its size due to the user moving the mouse.

  Note that \a rect may have a negative width or height, if the selection is
  being dragged to the upper or left side of the selection rect origin.
*/

/*! \fn void BaseChartSelectionRect::canceled(const QRect &rect, QInputEvent
  *event);

  This signal is emitted when the selection interaction was cancelled. Note that
  \a event is \c nullptr if the selection interaction was cancelled
  programmatically, by a call to \ref cancel.

  The user may cancel the selection interaction by pressing the escape key. In
  this case, \a event holds the respective input event.

  Note that \a rect may have a negative width or height, if the selection is
  being dragged to the upper or left side of the selection rect origin.
*/

/*! \fn void BaseChartSelectionRect::accepted(const QRect &rect, QMouseEvent
  *event);

  This signal is emitted when the selection interaction was completed by the
  user releasing the mouse button.

  Note that \a rect may have a negative width or height, if the selection is
  being dragged to the upper or left side of the selection rect origin.
*/

/* end documentation of signals */

/*!
  Creates a new BaseChartSelectionRect instance. To make BaseChartCustomPlot use
  the selection rect instance, pass it to \ref
  BaseChartCustomPlot::setSelectionRect. \a parentPlot should be set to the same
  BaseChartCustomPlot widget.
*/
BaseChartSelectionRect::BaseChartSelectionRect(BaseChartCustomPlot* parentPlot)
    : BaseChartLayerable(parentPlot),
      mPen(QBrush(Qt::gray), 0, Qt::DashLine),
      mBrush(Qt::NoBrush),
      mActive(false) {}

BaseChartSelectionRect::~BaseChartSelectionRect() { cancel(); }

/*!
  A convenience function which returns the coordinate range of the provided \a
  axis, that this selection rect currently encompasses.
*/
BaseChartRange BaseChartSelectionRect::range(const BaseChartAxis* axis) const {
  if (axis) {
    if (axis->orientation() == Qt::Horizontal)
      return {axis->pixelToCoord(mRect.left()),
              axis->pixelToCoord(mRect.left() + mRect.width())};
    else
      return {axis->pixelToCoord(mRect.top() + mRect.height()),
              axis->pixelToCoord(mRect.top())};
  } else {
    qDebug() << Q_FUNC_INFO << "called with axis zero";
    return {};
  }
}

/*!
  Sets the pen that will be used to draw the selection rect outline.

  \see setBrush
*/
void BaseChartSelectionRect::setPen(const QPen& pen) { mPen = pen; }

/*!
  Sets the brush that will be used to fill the selection rect. By default the
  selection rect is not filled, i.e. \a brush is <tt>Qt::NoBrush</tt>.

  \see setPen
*/
void BaseChartSelectionRect::setBrush(const QBrush& brush) { mBrush = brush; }

/*!
  If there is currently a selection interaction going on (\ref isActive), the
  interaction is canceled. The selection rect will emit the \ref canceled
  signal.
*/
void BaseChartSelectionRect::cancel() {
  if (mActive) {
    mActive = false;
    emit canceled(mRect, nullptr);
  }
}

/*! \internal

  This method is called by BaseChartCustomPlot to indicate that a selection rect
  interaction was initiated. The default implementation sets the selection rect
  to active, initializes the selection rect geometry and emits the \ref started
  signal.
*/
void BaseChartSelectionRect::startSelection(QMouseEvent* event) {
  mActive = true;
  mRect = QRect(event->pos(), event->pos());
  emit started(event);
}

/*! \internal

  This method is called by BaseChartCustomPlot to indicate that an ongoing
  selection rect interaction needs to update its geometry. The default
  implementation updates the rect and emits the \ref changed signal.
*/
void BaseChartSelectionRect::moveSelection(QMouseEvent* event) {
  mRect.setBottomRight(event->pos());
  emit changed(mRect, event);
  layer()->replot();
}

/*! \internal

  This method is called by BaseChartCustomPlot to indicate that an ongoing
  selection rect interaction has finished by the user releasing the mouse
  button. The default implementation deactivates the selection rect and emits
  the \ref accepted signal.
*/
void BaseChartSelectionRect::endSelection(QMouseEvent* event) {
  mRect.setBottomRight(event->pos());
  mActive = false;
  emit accepted(mRect, event);
}

/*! \internal

  This method is called by BaseChartCustomPlot when a key has been pressed by
  the user while the selection rect interaction is active. The default
  implementation allows to \ref cancel the interaction by hitting the escape
  key.
*/
void BaseChartSelectionRect::keyPressEvent(QKeyEvent* event) {
  if (event->key() == Qt::Key_Escape && mActive) {
    mActive = false;
    emit canceled(mRect, event);
  }
}

/* inherits documentation from base class */
void BaseChartSelectionRect::applyDefaultAntialiasingHint(
    BaseChartPainter* painter) const {
  applyAntialiasingHint(painter, mAntialiased, aeOther);
}

/*! \internal

  If the selection rect is active (\ref isActive), draws the selection rect
  defined by \a mRect.

  \seebaseclassmethod
*/
void BaseChartSelectionRect::draw(BaseChartPainter* painter) {
  if (mActive) {
    painter->setPen(mPen);
    painter->setBrush(mBrush);
    painter->drawRect(mRect);
  }
}

////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////// BaseChartSelectionDecorator
////////////////////////////////////////////////////////////////////////////////////////////////////

/*! \class BaseChartSelectionDecorator
  \brief Controls how a plottable's data selection is drawn

  Each \ref BaseChartAbstractPlottable instance has one \ref
  BaseChartSelectionDecorator (accessible via \ref
  BaseChartAbstractPlottable::selectionDecorator) and uses it when drawing
  selected segments of its data.

  The selection decorator controls both pen (\ref setPen) and brush (\ref
  setBrush), as well as the scatter style (\ref setScatterStyle) if the
  plottable draws scatters. Since a \ref BaseChartScatterStyle is itself
  composed of different properties such as color shape and size, the decorator
  allows specifying exactly which of those properties shall be used for the
  selected data point, via \ref setUsedScatterProperties.

  A \ref BaseChartSelectionDecorator subclass instance can be passed to a
  plottable via \ref BaseChartAbstractPlottable::setSelectionDecorator, allowing
  greater customizability of the appearance of selected segments.

  Use \ref copyFrom to easily transfer the settings of one decorator to another
  one. This is especially useful since plottables take ownership of the passed
  selection decorator, and thus the same decorator instance can not be passed to
  multiple plottables.

  Selection decorators can also themselves perform drawing operations by
  reimplementing \ref drawDecoration, which is called by the plottable's draw
  method. The base class \ref BaseChartSelectionDecorator does not make use of
  this however. For example, \ref QCPSelectionDecoratorBracket draws brackets
  around selected data segments.
*/

/*!
  Creates a new BaseChartSelectionDecorator instance with default values
*/
BaseChartSelectionDecorator::BaseChartSelectionDecorator()
    : mPen(QColor(80, 80, 255), 2.5),
      mBrush(Qt::NoBrush),
      mUsedScatterProperties(BaseChartScatterStyle::spNone),
      mPlottable(nullptr) {}

BaseChartSelectionDecorator::~BaseChartSelectionDecorator() {}

/*!
  Sets the pen that will be used by the parent plottable to draw selected data
  segments.
*/
void BaseChartSelectionDecorator::setPen(const QPen& pen) { mPen = pen; }

/*!
  Sets the brush that will be used by the parent plottable to draw selected data
  segments.
*/
void BaseChartSelectionDecorator::setBrush(const QBrush& brush) {
  mBrush = brush;
}

/*!
  Sets the scatter style that will be used by the parent plottable to draw
  scatters in selected data segments.

  \a usedProperties specifies which parts of the passed \a scatterStyle will be
  used by the plottable. The used properties can also be changed via \ref
  setUsedScatterProperties.
*/
void BaseChartSelectionDecorator::setScatterStyle(
    const BaseChartScatterStyle& scatterStyle,
    BaseChartScatterStyle::ScatterProperties usedProperties) {
  mScatterStyle = scatterStyle;
  setUsedScatterProperties(usedProperties);
}

/*!
  Use this method to define which properties of the scatter style (set via \ref
  setScatterStyle) will be used for selected data segments. All properties of
  the scatter style that are not specified in \a properties will remain as
  specified in the plottable's original scatter style.

  \see BaseChartScatterStyle::ScatterProperty
*/
void BaseChartSelectionDecorator::setUsedScatterProperties(
    const BaseChartScatterStyle::ScatterProperties& properties) {
  mUsedScatterProperties = properties;
}

/*!
  Sets the pen of \a painter to the pen of this selection decorator.

  \see applyBrush, getFinalScatterStyle
*/
void BaseChartSelectionDecorator::applyPen(BaseChartPainter* painter) const {
  painter->setPen(mPen);
}

/*!
  Sets the brush of \a painter to the brush of this selection decorator.

  \see applyPen, getFinalScatterStyle
*/
void BaseChartSelectionDecorator::applyBrush(BaseChartPainter* painter) const {
  painter->setBrush(mBrush);
}

/*!
  Returns the scatter style that the parent plottable shall use for selected
  scatter points. The plottable's original (unselected) scatter style must be
  passed as \a unselectedStyle. Depending on the setting of \ref
  setUsedScatterProperties, the returned scatter style is a mixture of this
  selecion decorator's scatter style (\ref setScatterStyle), and \a
  unselectedStyle.

  \see applyPen, applyBrush, setScatterStyle
*/
BaseChartScatterStyle BaseChartSelectionDecorator::getFinalScatterStyle(
    const BaseChartScatterStyle& unselectedStyle) const {
  BaseChartScatterStyle result(unselectedStyle);
  result.setFromOther(mScatterStyle, mUsedScatterProperties);

  // if style shall inherit pen from plottable (has no own pen defined), give it
  // the selected plottable pen explicitly, so it doesn't use the unselected
  // plottable pen when used in the plottable:
  if (!result.isPenDefined()) result.setPen(mPen);

  return result;
}

/*!
  Copies all properties (e.g. color, fill, scatter style) of the \a other
  selection decorator to this selection decorator.
*/
void BaseChartSelectionDecorator::copyFrom(
    const BaseChartSelectionDecorator* other) {
  setPen(other->pen());
  setBrush(other->brush());
  setScatterStyle(other->scatterStyle(), other->usedScatterProperties());
}

/*!
  This method is called by all plottables' draw methods to allow custom
  selection decorations to be drawn. Use the passed \a painter to perform the
  drawing operations. \a selection carries the data selection for which the
  decoration shall be drawn.

  The default base class implementation of \ref BaseChartSelectionDecorator has
  no special decoration, so this method does nothing.
*/
void BaseChartSelectionDecorator::drawDecoration(
    BaseChartPainter* painter, BaseChartDataSelection selection) {
  Q_UNUSED(painter)
  Q_UNUSED(selection)
}

/*! \internal

  This method is called as soon as a selection decorator is associated with a
  plottable, by a call to \ref
  BaseChartAbstractPlottable::setSelectionDecorator. This way the selection
  decorator can obtain a pointer to the plottable that uses it (e.g. to access
  data points via the \ref BaseChartAbstractPlottable::interface1D interface).

  If the selection decorator was already added to a different plottable before,
  this method aborts the registration and returns false.
*/
bool BaseChartSelectionDecorator::registerWithPlottable(
    BaseChartAbstractPlottable* plottable) {
  if (!mPlottable) {
    mPlottable = plottable;
    return true;
  } else {
    qDebug() << Q_FUNC_INFO
             << "This selection decorator is already registered with plottable:"
             << reinterpret_cast<quintptr>(mPlottable);
    return false;
  }
}